summaryrefslogtreecommitdiff
path: root/app/[lng]/sales
diff options
context:
space:
mode:
Diffstat (limited to 'app/[lng]/sales')
-rw-r--r--app/[lng]/sales/(sales)/b-rfq/[id]/final/page.tsx0
-rw-r--r--app/[lng]/sales/(sales)/b-rfq/[id]/initial/page.tsx52
-rw-r--r--app/[lng]/sales/(sales)/b-rfq/[id]/layout.tsx87
-rw-r--r--app/[lng]/sales/(sales)/b-rfq/[id]/page.tsx53
-rw-r--r--app/[lng]/sales/(sales)/b-rfq/page.tsx79
-rw-r--r--app/[lng]/sales/(sales)/basic-contract-template/page.tsx74
-rw-r--r--app/[lng]/sales/(sales)/basic-contract/page.tsx74
-rw-r--r--app/[lng]/sales/(sales)/email-template/[name]/page.tsx26
-rw-r--r--app/[lng]/sales/(sales)/email-template/page.tsx19
-rw-r--r--app/[lng]/sales/(sales)/equip-class/page.tsx75
-rw-r--r--app/[lng]/sales/(sales)/form-list/page.tsx75
-rw-r--r--app/[lng]/sales/(sales)/incoterms/page.tsx53
-rw-r--r--app/[lng]/sales/(sales)/menu-list/page.tsx70
-rw-r--r--app/[lng]/sales/(sales)/payment-conditions/page.tsx53
-rw-r--r--app/[lng]/sales/(sales)/po-rfq/page.tsx61
-rw-r--r--app/[lng]/sales/(sales)/po/page.tsx65
-rw-r--r--app/[lng]/sales/(sales)/poa/page.tsx61
-rw-r--r--app/[lng]/sales/(sales)/pq-criteria/[id]/page.tsx81
-rw-r--r--app/[lng]/sales/(sales)/pq-criteria/page.tsx70
-rw-r--r--app/[lng]/sales/(sales)/pq/[vendorId]/page.tsx108
-rw-r--r--app/[lng]/sales/(sales)/pq/page.tsx71
-rw-r--r--app/[lng]/sales/(sales)/pq_new/[vendorId]/[submissionId]/page.tsx215
-rw-r--r--app/[lng]/sales/(sales)/pq_new/page.tsx96
-rw-r--r--app/[lng]/sales/(sales)/report/page.tsx69
-rw-r--r--app/[lng]/sales/(sales)/rfq/[id]/cbe/page.tsx55
-rw-r--r--app/[lng]/sales/(sales)/rfq/[id]/layout.tsx89
-rw-r--r--app/[lng]/sales/(sales)/rfq/[id]/page.tsx55
-rw-r--r--app/[lng]/sales/(sales)/rfq/[id]/tbe/page.tsx55
-rw-r--r--app/[lng]/sales/(sales)/rfq/page.tsx80
-rw-r--r--app/[lng]/sales/(sales)/tag-numbering/page.tsx74
-rw-r--r--app/[lng]/sales/(sales)/tasks/page.tsx63
-rw-r--r--app/[lng]/sales/(sales)/vendor-check-list/page.tsx74
-rw-r--r--app/[lng]/sales/(sales)/vendor-investigation/page.tsx65
-rw-r--r--app/[lng]/sales/(sales)/vendor-type/page.tsx70
-rw-r--r--app/[lng]/sales/(sales)/vendors/[id]/info/items/page.tsx56
-rw-r--r--app/[lng]/sales/(sales)/vendors/[id]/info/layout.tsx94
-rw-r--r--app/[lng]/sales/(sales)/vendors/[id]/info/materials/page.tsx56
-rw-r--r--app/[lng]/sales/(sales)/vendors/[id]/info/page.tsx56
-rw-r--r--app/[lng]/sales/(sales)/vendors/[id]/info/rfq-history/page.tsx55
-rw-r--r--app/[lng]/sales/(sales)/vendors/page.tsx78
40 files changed, 20 insertions, 2742 deletions
diff --git a/app/[lng]/sales/(sales)/b-rfq/[id]/final/page.tsx b/app/[lng]/sales/(sales)/b-rfq/[id]/final/page.tsx
deleted file mode 100644
index e69de29b..00000000
--- a/app/[lng]/sales/(sales)/b-rfq/[id]/final/page.tsx
+++ /dev/null
diff --git a/app/[lng]/sales/(sales)/b-rfq/[id]/initial/page.tsx b/app/[lng]/sales/(sales)/b-rfq/[id]/initial/page.tsx
deleted file mode 100644
index 1af65fbc..00000000
--- a/app/[lng]/sales/(sales)/b-rfq/[id]/initial/page.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import { Separator } from "@/components/ui/separator"
-import { type SearchParams } from "@/types/table"
-import { getValidFilters } from "@/lib/data-table"
-import { InitialRfqDetailTable } from "@/lib/b-rfq/initial/initial-rfq-detail-table"
-import { getInitialRfqDetail } from "@/lib/b-rfq/service"
-import { searchParamsInitialRfqDetailCache } from "@/lib/b-rfq/validations"
-
-interface IndexPageProps {
- // Next.js 13 App Router에서 기본으로 주어지는 객체들
- params: {
- lng: string
- id: string
- }
- searchParams: Promise<SearchParams>
-}
-
-export default async function RfqPage(props: IndexPageProps) {
- const resolvedParams = await props.params
- const lng = resolvedParams.lng
- const id = resolvedParams.id
-
- const idAsNumber = Number(id)
-
- // 2) SearchParams 파싱 (Zod)
- // - "filters", "page", "perPage", "sort" 등 contact 전용 컬럼
- const searchParams = await props.searchParams
- const search = searchParamsInitialRfqDetailCache.parse(searchParams)
- const validFilters = getValidFilters(search.filters)
-
- const promises = getInitialRfqDetail({
- ...search,
- filters: validFilters,
- }, idAsNumber)
-
- // 4) 렌더링
- return (
- <div className="space-y-6">
- <div>
- <h3 className="text-lg font-medium">
- Initial RFQ List
- </h3>
- <p className="text-sm text-muted-foreground">
- 설계로부터 받은 RFQ 문서와 구매 RFQ 문서 및 사전 계약자료를 Vendor에 발송하기 위한 RFQ 생성 및 관리하는 화면입니다.
- </p>
- </div>
- <Separator />
- <div>
- <InitialRfqDetailTable promises={promises} rfqId={idAsNumber}/>
- </div>
- </div>
- )
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/b-rfq/[id]/layout.tsx b/app/[lng]/sales/(sales)/b-rfq/[id]/layout.tsx
deleted file mode 100644
index 8dad7676..00000000
--- a/app/[lng]/sales/(sales)/b-rfq/[id]/layout.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-import { Metadata } from "next"
-import Link from "next/link"
-import { Separator } from "@/components/ui/separator"
-import { SidebarNav } from "@/components/layout/sidebar-nav"
-import { formatDate } from "@/lib/utils"
-import { Button } from "@/components/ui/button"
-import { ArrowLeft } from "lucide-react"
-import { RfqDashboardView } from "@/db/schema"
-import { findBRfqById } from "@/lib/b-rfq/service"
-
-export const metadata: Metadata = {
- title: "견적 RFQ 상세",
-}
-
-export default async function RfqLayout({
- children,
- params,
-}: {
- children: React.ReactNode
- params: { lng: string, id: string }
-}) {
-
- // 1) URL 파라미터에서 id 추출, Number로 변환
- const resolvedParams = await params
- const lng = resolvedParams.lng
- const id = resolvedParams.id
-
- const idAsNumber = Number(id)
- // 2) DB에서 해당 협력업체 정보 조회
- const rfq: RfqDashboardView | null = await findBRfqById(idAsNumber)
-
- // 3) 사이드바 메뉴
- const sidebarNavItems = [
- {
- title: "견적/입찰 문서관리",
- href: `/${lng}/evcp/b-rfq/${id}`,
- },
- {
- title: "Initial RFQ 발송",
- href: `/${lng}/evcp/b-rfq/${id}/initial`,
- },
- {
- title: "Final RFQ 발송",
- href: `/${lng}/evcp/b-rfq/${id}/final`,
- },
-
- ]
-
- return (
- <>
- <div className="container py-6">
- <section className="overflow-hidden rounded-[0.5rem] border bg-background shadow">
- <div className="hidden space-y-6 p-10 pb-16 md:block">
- <div className="flex items-center justify-end mb-4">
- <Link href={`/${lng}/evcp/b-rfq`} passHref>
- <Button variant="ghost" className="flex items-center text-primary hover:text-primary/80 transition-colors p-0 h-auto">
- <ArrowLeft className="mr-1 h-4 w-4" />
- <span>RFQ 목록으로 돌아가기</span>
- </Button>
- </Link>
- </div>
- <div className="space-y-0.5">
- {/* 4) 협력업체 정보가 있으면 코드 + 이름 + "상세 정보" 표기 */}
- <h2 className="text-2xl font-bold tracking-tight">
- {rfq
- ? `${rfq.rfqCode ?? ""} | ${rfq.packageNo ?? ""} | ${rfq.packageName ?? ""}`
- : "Loading RFQ..."}
- </h2>
-
- <p className="text-muted-foreground">
- PR발행 전 RFQ를 생성하여 관리하는 화면입니다.
- </p>
- <h3>Due Date:{rfq && rfq?.dueDate && <strong>{formatDate(rfq?.dueDate)}</strong>}</h3>
- </div>
- <Separator className="my-6" />
- <div className="flex flex-col space-y-8 lg:flex-row lg:space-x-12 lg:space-y-0">
- <aside className="lg:w-64 flex-shrink-0">
- <SidebarNav items={sidebarNavItems} />
- </aside>
- <div className="lg:w-[calc(100%-16rem)] overflow-auto">{children}</div>
- </div>
- </div>
- </section>
- </div>
- </>
- )
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/b-rfq/[id]/page.tsx b/app/[lng]/sales/(sales)/b-rfq/[id]/page.tsx
deleted file mode 100644
index 26dc45fb..00000000
--- a/app/[lng]/sales/(sales)/b-rfq/[id]/page.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import { Separator } from "@/components/ui/separator"
-import { type SearchParams } from "@/types/table"
-import { getValidFilters } from "@/lib/data-table"
-import { searchParamsRfqAttachmentsCache } from "@/lib/b-rfq/validations"
-import { getRfqAttachments } from "@/lib/b-rfq/service"
-import { RfqAttachmentsTable } from "@/lib/b-rfq/attachment/attachment-table"
-
-interface IndexPageProps {
- // Next.js 13 App Router에서 기본으로 주어지는 객체들
- params: {
- lng: string
- id: string
- }
- searchParams: Promise<SearchParams>
-}
-
-export default async function RfqPage(props: IndexPageProps) {
- const resolvedParams = await props.params
- const lng = resolvedParams.lng
- const id = resolvedParams.id
-
- const idAsNumber = Number(id)
-
-
- // 2) SearchParams 파싱 (Zod)
- // - "filters", "page", "perPage", "sort" 등 contact 전용 컬럼
- const searchParams = await props.searchParams
- const search = searchParamsRfqAttachmentsCache.parse(searchParams)
- const validFilters = getValidFilters(search.filters)
-
- const promises = getRfqAttachments({
- ...search,
- filters: validFilters,
- }, idAsNumber)
-
- // 4) 렌더링
- return (
- <div className="space-y-6">
- <div>
- <h3 className="text-lg font-medium">
- 견적 RFQ 문서관리
- </h3>
- <p className="text-sm text-muted-foreground">
- 설계로부터 받은 RFQ 문서와 구매 RFQ 문서를 관리하고 Vendor 회신을 점검/관리하는 화면입니다.
- </p>
- </div>
- <Separator />
- <div>
- <RfqAttachmentsTable promises={promises} rfqId={idAsNumber} />
- </div>
- </div>
- )
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/b-rfq/page.tsx b/app/[lng]/sales/(sales)/b-rfq/page.tsx
deleted file mode 100644
index a66d7b58..00000000
--- a/app/[lng]/sales/(sales)/b-rfq/page.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-import * as React from "react"
-import { Metadata } from "next"
-import { type SearchParams } from "@/types/table"
-import { getValidFilters } from "@/lib/data-table"
-import { Shell } from "@/components/shell"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { searchParamsRFQDashboardCache } from "@/lib/b-rfq/validations"
-import { getRFQDashboard } from "@/lib/b-rfq/service"
-import { RFQDashboardTable } from "@/lib/b-rfq/summary-table/summary-rfq-table"
-
-export const metadata: Metadata = {
- title: "견적 RFQ",
- description: "",
-}
-
-interface PQReviewPageProps {
- searchParams: Promise<SearchParams>
-}
-
-export default async function PQReviewPage(props: PQReviewPageProps) {
- const searchParams = await props.searchParams
- const search = searchParamsRFQDashboardCache.parse(searchParams)
- const validFilters = getValidFilters(search.filters)
-
- // 기본 필터 처리 (통일된 이름 사용)
- let basicFilters = []
- if (search.basicFilters && search.basicFilters.length > 0) {
- basicFilters = search.basicFilters
- console.log("Using search.basicFilters:", basicFilters);
- } else {
- console.log("No basic filters found");
- }
-
- // 모든 필터를 합쳐서 처리
- const allFilters = [...validFilters, ...basicFilters]
-
- // 조인 연산자도 통일된 이름 사용
- const joinOperator = search.basicJoinOperator || search.joinOperator || 'and';
-
- // Promise.all로 감싸서 전달
- const promises = Promise.all([
- getRFQDashboard({
- ...search,
- filters: allFilters,
- joinOperator,
- })
- ])
-
- console.log(search, "견적")
-
- return (
- <Shell className="gap-4">
- <div className="flex items-center justify-between space-y-2">
- <div className="flex items-center justify-between space-y-2">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- 견적 RFQ
- </h2>
- </div>
- </div>
- </div>
-
- {/* Items처럼 직접 테이블 렌더링 */}
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={8}
- searchableColumnCount={2}
- filterableColumnCount={3}
- cellWidths={["10rem", "15rem", "12rem", "12rem", "8rem", "8rem", "10rem", "8rem"]}
- shrinkZero
- />
- }
- >
- <RFQDashboardTable promises={promises} />
- </React.Suspense>
- </Shell>
- )
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/basic-contract-template/page.tsx b/app/[lng]/sales/(sales)/basic-contract-template/page.tsx
deleted file mode 100644
index adc57ed9..00000000
--- a/app/[lng]/sales/(sales)/basic-contract-template/page.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-import * as React from "react"
-import { type SearchParams } from "@/types/table"
-
-import { getValidFilters } from "@/lib/data-table"
-import { Skeleton } from "@/components/ui/skeleton"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { Shell } from "@/components/shell"
-import { getBasicContractTemplates } from "@/lib/basic-contract/service"
-import { searchParamsTemplatesCache } from "@/lib/basic-contract/validations"
-import { BasicContractTemplateTable } from "@/lib/basic-contract/template/basic-contract-template"
-
-
-interface IndexPageProps {
- searchParams: Promise<SearchParams>
-}
-
-export default async function IndexPage(props: IndexPageProps) {
- const searchParams = await props.searchParams
- const search = searchParamsTemplatesCache.parse(searchParams)
-
- const validFilters = getValidFilters(search.filters)
-
- const promises = Promise.all([
- getBasicContractTemplates({
- ...search,
- filters: validFilters,
- }),
-
- ])
-
- return (
- <Shell className="gap-2">
- <div className="flex items-center justify-between space-y-2">
- <div className="flex items-center justify-between space-y-2">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- 기본계약서 템플릿 관리
- </h2>
- <p className="text-muted-foreground">
- 기본계약서를 비롯하여 초기 서명이 필요한 문서를 등록하고 편집할 수 있습니다. 활성화된 템플릿이 서명 요청의 리스트에 나타나게 됩니다..{" "}
- {/* <span className="inline-flex items-center whitespace-nowrap">
- <Ellipsis className="size-3" />
- <span className="ml-1">버튼</span>
- </span>
- 을 통해 담당자 연락처, 입찰 이력, 계약 이력, 패키지 내용 등을 확인 할 수 있습니다. */}
- </p>
- </div>
- </div>
- </div>
-
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}>
- {/* <DateRangePicker
- triggerSize="sm"
- triggerClassName="ml-auto w-56 sm:w-60"
- align="end"
- shallow={false}
- /> */}
- </React.Suspense>
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={6}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
- shrinkZero
- />
- }
- >
- <BasicContractTemplateTable promises={promises} />
- </React.Suspense>
- </Shell>
- )
-}
diff --git a/app/[lng]/sales/(sales)/basic-contract/page.tsx b/app/[lng]/sales/(sales)/basic-contract/page.tsx
deleted file mode 100644
index a043e530..00000000
--- a/app/[lng]/sales/(sales)/basic-contract/page.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-import * as React from "react"
-import { type SearchParams } from "@/types/table"
-
-import { getValidFilters } from "@/lib/data-table"
-import { Skeleton } from "@/components/ui/skeleton"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { Shell } from "@/components/shell"
-import { getBasicContracts } from "@/lib/basic-contract/service"
-import { searchParamsCache } from "@/lib/basic-contract/validations"
-import { BasicContractsTable } from "@/lib/basic-contract/status/basic-contract-table"
-
-
-interface IndexPageProps {
- searchParams: Promise<SearchParams>
-}
-
-export default async function IndexPage(props: IndexPageProps) {
- const searchParams = await props.searchParams
- const search = searchParamsCache.parse(searchParams)
-
- const validFilters = getValidFilters(search.filters)
-
- const promises = Promise.all([
- getBasicContracts({
- ...search,
- filters: validFilters,
- }),
-
- ])
-
- return (
- <Shell className="gap-2">
- <div className="flex items-center justify-between space-y-2">
- <div className="flex items-center justify-between space-y-2">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- 기본계약서 서명 현황
- </h2>
- <p className="text-muted-foreground">
- 기본계약서를 비롯하여 초기 서명이 필요한 문서의 서명 현황을 확인할 수 있고 서명된 문서들을 다운로드할 수 있습니다. {" "}
- {/* <span className="inline-flex items-center whitespace-nowrap">
- <Ellipsis className="size-3" />
- <span className="ml-1">버튼</span>
- </span>
- 을 통해 담당자 연락처, 입찰 이력, 계약 이력, 패키지 내용 등을 확인 할 수 있습니다. */}
- </p>
- </div>
- </div>
- </div>
-
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}>
- {/* <DateRangePicker
- triggerSize="sm"
- triggerClassName="ml-auto w-56 sm:w-60"
- align="end"
- shallow={false}
- /> */}
- </React.Suspense>
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={6}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
- shrinkZero
- />
- }
- >
- <BasicContractsTable promises={promises} />
- </React.Suspense>
- </Shell>
- )
-}
diff --git a/app/[lng]/sales/(sales)/email-template/[name]/page.tsx b/app/[lng]/sales/(sales)/email-template/[name]/page.tsx
deleted file mode 100644
index cccc10fc..00000000
--- a/app/[lng]/sales/(sales)/email-template/[name]/page.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { getTemplateAction } from '@/lib/mail/service';
-import MailTemplateEditorClient from '@/components/mail/mail-template-editor-client';
-
-interface EditMailTemplatePageProps {
- params: {
- name: string;
- lng: string;
- };
-}
-
-export default async function EditMailTemplatePage({ params }: EditMailTemplatePageProps) {
- const { name: templateName } = await params;
-
- // 서버에서 초기 템플릿 데이터 가져오기
- const result = await getTemplateAction(templateName);
- const initialTemplate = result.success ? result.data : null;
-
- return (
- <div className="container mx-auto p-6">
- <MailTemplateEditorClient
- templateName={templateName}
- initialTemplate={initialTemplate}
- />
- </div>
- );
-}
diff --git a/app/[lng]/sales/(sales)/email-template/page.tsx b/app/[lng]/sales/(sales)/email-template/page.tsx
deleted file mode 100644
index 1ef3de6c..00000000
--- a/app/[lng]/sales/(sales)/email-template/page.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import { getTemplatesAction } from '@/lib/mail/service';
-import MailTemplatesClient from '@/components/mail/mail-templates-client';
-
-export default async function MailTemplatesPage() {
- // 서버에서 초기 데이터 가져오기
- const result = await getTemplatesAction();
- const initialData = result.success ? result.data : [];
-
- return (
- <div className="container mx-auto p-6">
- <div className="mb-8">
- <h1 className="text-3xl font-bold text-gray-900 mb-2">메일 템플릿 관리</h1>
- <p className="text-gray-600">이메일 템플릿을 관리할 수 있습니다.</p>
- </div>
-
- <MailTemplatesClient initialData={initialData} />
- </div>
- );
-}
diff --git a/app/[lng]/sales/(sales)/equip-class/page.tsx b/app/[lng]/sales/(sales)/equip-class/page.tsx
deleted file mode 100644
index cfa8f133..00000000
--- a/app/[lng]/sales/(sales)/equip-class/page.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-import * as React from "react"
-import { type SearchParams } from "@/types/table"
-
-import { getValidFilters } from "@/lib/data-table"
-import { Skeleton } from "@/components/ui/skeleton"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { Shell } from "@/components/shell"
-import { searchParamsCache } from "@/lib/equip-class/validation"
-import { FormListsTable } from "@/lib/form-list/table/formLists-table"
-import { getTagClassists } from "@/lib/equip-class/service"
-import { EquipClassTable } from "@/lib/equip-class/table/equipClass-table"
-
-
-interface IndexPageProps {
- searchParams: Promise<SearchParams>
-}
-
-export default async function IndexPage(props: IndexPageProps) {
- const searchParams = await props.searchParams
- const search = searchParamsCache.parse(searchParams)
-
- const validFilters = getValidFilters(search.filters)
-
- const promises = Promise.all([
- getTagClassists({
- ...search,
- filters: validFilters,
- }),
-
- ])
-
- return (
- <Shell className="gap-2">
- <div className="flex items-center justify-between space-y-2">
- <div className="flex items-center justify-between space-y-2">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- 객체 클래스 목록 from S-EDP
- </h2>
- <p className="text-muted-foreground">
- 객체 클래스 목록을 확인할 수 있습니다.{" "}
- {/* <span className="inline-flex items-center whitespace-nowrap">
- <Ellipsis className="size-3" />
- <span className="ml-1">버튼</span>
- </span>
- 을 통해 담당자 연락처, 입찰 이력, 계약 이력, 패키지 내용 등을 확인 할 수 있습니다. */}
- </p>
- </div>
- </div>
- </div>
-
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}>
- {/* <DateRangePicker
- triggerSize="sm"
- triggerClassName="ml-auto w-56 sm:w-60"
- align="end"
- shallow={false}
- /> */}
- </React.Suspense>
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={6}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
- shrinkZero
- />
- }
- >
- <EquipClassTable promises={promises} />
- </React.Suspense>
- </Shell>
- )
-}
diff --git a/app/[lng]/sales/(sales)/form-list/page.tsx b/app/[lng]/sales/(sales)/form-list/page.tsx
deleted file mode 100644
index a6cf7d9e..00000000
--- a/app/[lng]/sales/(sales)/form-list/page.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-import * as React from "react"
-import { type SearchParams } from "@/types/table"
-
-import { getValidFilters } from "@/lib/data-table"
-import { Skeleton } from "@/components/ui/skeleton"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { Shell } from "@/components/shell"
-import { searchParamsCache } from "@/lib/form-list/validation"
-import { ItemsTable } from "@/lib/items/table/items-table"
-import { getFormLists } from "@/lib/form-list/service"
-import { FormListsTable } from "@/lib/form-list/table/formLists-table"
-
-
-interface IndexPageProps {
- searchParams: Promise<SearchParams>
-}
-
-export default async function IndexPage(props: IndexPageProps) {
- const searchParams = await props.searchParams
- const search = searchParamsCache.parse(searchParams)
-
- const validFilters = getValidFilters(search.filters)
-
- const promises = Promise.all([
- getFormLists({
- ...search,
- filters: validFilters,
- }),
-
- ])
-
- return (
- <Shell className="gap-2">
- <div className="flex items-center justify-between space-y-2">
- <div className="flex items-center justify-between space-y-2">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- 레지스터 목록 from S-EDP
- </h2>
- <p className="text-muted-foreground">
- 협력업체 데이터 입력을 위한 레지스터 목록 리스트입니다.{" "}
- {/* <span className="inline-flex items-center whitespace-nowrap">
- <Ellipsis className="size-3" />
- <span className="ml-1">버튼</span>
- </span>
- 을 통해 담당자 연락처, 입찰 이력, 계약 이력, 패키지 내용 등을 확인 할 수 있습니다. */}
- </p>
- </div>
- </div>
- </div>
-
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}>
- {/* <DateRangePicker
- triggerSize="sm"
- triggerClassName="ml-auto w-56 sm:w-60"
- align="end"
- shallow={false}
- /> */}
- </React.Suspense>
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={6}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
- shrinkZero
- />
- }
- >
- <FormListsTable promises={promises} />
- </React.Suspense>
- </Shell>
- )
-}
diff --git a/app/[lng]/sales/(sales)/incoterms/page.tsx b/app/[lng]/sales/(sales)/incoterms/page.tsx
deleted file mode 100644
index 57a19009..00000000
--- a/app/[lng]/sales/(sales)/incoterms/page.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import * as React from "react";
-import { type SearchParams } from "@/types/table";
-import { getValidFilters } from "@/lib/data-table";
-import { Shell } from "@/components/shell";
-import { Skeleton } from "@/components/ui/skeleton";
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton";
-import { SearchParamsCache } from "@/lib/incoterms/validations";
-import { getIncoterms } from "@/lib/incoterms/service";
-import { IncotermsTable } from "@/lib/incoterms/table/incoterms-table";
-
-interface IndexPageProps {
- searchParams: Promise<SearchParams>;
-}
-
-export default async function IndexPage(props: IndexPageProps) {
- const searchParams = await props.searchParams;
- const search = SearchParamsCache.parse(searchParams);
- const validFilters = getValidFilters(search.filters);
-
- const promises = Promise.all([
- getIncoterms({
- ...search,
- filters: validFilters,
- }),
- ]);
-
- return (
- <Shell className="gap-2">
- <div className="flex items-center justify-between space-y-2">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">인코텀즈 관리</h2>
- <p className="text-muted-foreground">
- 인코텀즈(Incoterms)를 등록, 수정, 삭제할 수 있습니다.
- </p>
- </div>
- </div>
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}></React.Suspense>
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={4}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "8rem"]}
- shrinkZero
- />
- }
- >
- <IncotermsTable promises={promises} />
- </React.Suspense>
- </Shell>
- );
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/menu-list/page.tsx b/app/[lng]/sales/(sales)/menu-list/page.tsx
deleted file mode 100644
index 84138320..00000000
--- a/app/[lng]/sales/(sales)/menu-list/page.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-// app/evcp/menu-list/page.tsx
-
-import { Suspense } from "react";
-import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
-import { Button } from "@/components/ui/button";
-import { RefreshCw, Settings } from "lucide-react";
-import { getActiveUsers, getMenuAssignments } from "@/lib/menu-list/servcie";
-import { InitializeButton } from "@/lib/menu-list/table/initialize-button";
-import { MenuListTable } from "@/lib/menu-list/table/menu-list-table";
-import { Shell } from "@/components/shell"
-import * as React from "react"
-
-export default async function MenuListPage() {
- // 초기 데이터 로드
- const [menusResult, usersResult] = await Promise.all([
- getMenuAssignments(),
- getActiveUsers()
- ]);
-
- return (
- <Shell className="gap-2">
- <div className="flex items-center justify-between space-y-2">
- <div className="flex items-center justify-between space-y-2">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- 메뉴 관리
- </h2>
- <p className="text-muted-foreground">
- 각 메뉴별로 담당자를 지정하고 관리할 수 있습니다.
- </p>
- </div>
- </div>
-
- </div>
-
-
- <React.Suspense
- fallback={
- ""
- }
- >
- <Card>
- <CardHeader>
- <CardTitle className="flex items-center gap-2">
- <Settings className="h-5 w-5" />
- 메뉴 리스트
- </CardTitle>
- <CardDescription>
- 시스템의 모든 메뉴와 담당자 정보를 확인할 수 있습니다.
- {menusResult.data?.length > 0 && (
- <span className="ml-2 text-sm">
- 총 {menusResult.data.length}개의 메뉴
- </span>
- )}
- </CardDescription>
- </CardHeader>
- <CardContent>
- <Suspense fallback={<div className="text-center py-8">로딩 중...</div>}>
- <MenuListTable
- initialMenus={menusResult.data || []}
- initialUsers={usersResult.data || []}
- />
- </Suspense>
- </CardContent>
- </Card>
- </React.Suspense>
- </Shell>
-
- );
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/payment-conditions/page.tsx b/app/[lng]/sales/(sales)/payment-conditions/page.tsx
deleted file mode 100644
index b9aedfbb..00000000
--- a/app/[lng]/sales/(sales)/payment-conditions/page.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import * as React from "react";
-import { type SearchParams } from "@/types/table";
-import { getValidFilters } from "@/lib/data-table";
-import { Shell } from "@/components/shell";
-import { Skeleton } from "@/components/ui/skeleton";
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton";
-import { SearchParamsCache } from "@/lib/payment-terms/validations";
-import { getPaymentTerms } from "@/lib/payment-terms/service";
-import { PaymentTermsTable } from "@/lib/payment-terms/table/payment-terms-table";
-
-interface IndexPageProps {
- searchParams: Promise<SearchParams>;
-}
-
-export default async function IndexPage(props: IndexPageProps) {
- const searchParams = await props.searchParams;
- const search = SearchParamsCache.parse(searchParams);
- const validFilters = getValidFilters(search.filters);
-
- const promises = Promise.all([
- getPaymentTerms({
- ...search,
- filters: validFilters,
- }),
- ]);
-
- return (
- <Shell className="gap-2">
- <div className="flex items-center justify-between space-y-2">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">결제 조건 관리</h2>
- <p className="text-muted-foreground">
- 결제 조건(Payment Terms)을 등록, 수정, 삭제할 수 있습니다.
- </p>
- </div>
- </div>
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}></React.Suspense>
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={4}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "8rem"]}
- shrinkZero
- />
- }
- >
- <PaymentTermsTable promises={promises} />
- </React.Suspense>
- </Shell>
- );
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/po-rfq/page.tsx b/app/[lng]/sales/(sales)/po-rfq/page.tsx
deleted file mode 100644
index bdeae25e..00000000
--- a/app/[lng]/sales/(sales)/po-rfq/page.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import { getPORfqs } from "@/lib/procurement-rfqs/services"
-import { searchParamsCache } from "@/lib/procurement-rfqs/validations"
-import { getValidFilters } from "@/lib/data-table"
-import { Shell } from "@/components/shell"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { RFQListTable } from "@/lib/procurement-rfqs/table/rfq-table"
-import { type SearchParams } from "@/types/table"
-import * as React from "react"
-
-interface RfqPageProps {
- searchParams: Promise<SearchParams>
-}
-
-export default async function RfqPage(props: RfqPageProps) {
- // searchParams를 await하여 resolve
- const searchParams = await props.searchParams
-
- // 파라미터 파싱
- const search = searchParamsCache.parse(searchParams);
- const validFilters = getValidFilters(search.filters);
-
- // RFQ 서버는 기본필터와 고급필터를 분리해서 받으므로 그대로 전달
- const promises = Promise.all([
- getPORfqs({
- ...search, // 모든 파라미터 전달 (page, perPage, sort, basicFilters, filters 등)
- filters: validFilters, // 고급 필터를 명시적으로 오버라이드 (파싱된 버전)
- })
- ])
-
- return (
- <Shell variant="fullscreen" className="h-full"> {/* fullscreen variant 사용 */}
- {/* 고정 헤더 영역 */}
- <div className="flex-shrink-0">
- <div className="flex items-center justify-between">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- 발주용 견적
- </h2>
- </div>
- </div>
- </div>
-
- {/* 테이블 영역 - 남은 공간 모두 차지 */}
- <div className="flex-1 min-h-0">
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={8}
- searchableColumnCount={2}
- filterableColumnCount={3}
- cellWidths={["10rem", "15rem", "12rem", "12rem", "8rem", "8rem", "10rem", "8rem"]}
- shrinkZero
- />
- }
- >
- <RFQListTable promises={promises} className="h-full" />
- </React.Suspense>
- </div>
- </Shell>
- )
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/po/page.tsx b/app/[lng]/sales/(sales)/po/page.tsx
deleted file mode 100644
index 7868e231..00000000
--- a/app/[lng]/sales/(sales)/po/page.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import * as React from "react"
-import { type SearchParams } from "@/types/table"
-
-import { getValidFilters } from "@/lib/data-table"
-import { Skeleton } from "@/components/ui/skeleton"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { Shell } from "@/components/shell"
-import { getPOs } from "@/lib/po/service"
-import { searchParamsCache } from "@/lib/po/validations"
-import { PoListsTable } from "@/lib/po/table/po-table"
-
-
-interface IndexPageProps {
- searchParams: Promise<SearchParams>
-}
-
-export default async function IndexPage(props: IndexPageProps) {
- const searchParams = await props.searchParams
- const search = searchParamsCache.parse(searchParams)
-
- const validFilters = getValidFilters(search.filters)
-
- const promises = Promise.all([
- getPOs({
- ...search,
- filters: validFilters,
- }),
- ])
-
- return (
- <Shell className="gap-2">
-
- <div className="flex items-center justify-between space-y-2">
- <div className="flex items-center justify-between space-y-2">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- PO 확인 및 전자서명
- </h2>
- <p className="text-muted-foreground">
- 기간계 시스템으로부터 PO를 확인하고 협력업체에게 전자서명을 요청할 수 있습니다. 요쳥된 전자서명의 이력 또한 확인할 수 있습니다.
-
- </p>
- </div>
- </div>
- </div>
-
-
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}>
- </React.Suspense>
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={6}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
- shrinkZero
- />
- }
- >
- <PoListsTable promises={promises} />
- </React.Suspense>
- </Shell>
- )
-}
diff --git a/app/[lng]/sales/(sales)/poa/page.tsx b/app/[lng]/sales/(sales)/poa/page.tsx
deleted file mode 100644
index dec5e05b..00000000
--- a/app/[lng]/sales/(sales)/poa/page.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import * as React from "react"
-import { type SearchParams } from "@/types/table"
-
-import { getValidFilters } from "@/lib/data-table"
-import { Skeleton } from "@/components/ui/skeleton"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { Shell } from "@/components/shell"
-import { getChangeOrders } from "@/lib/poa/service"
-import { searchParamsCache } from "@/lib/poa/validations"
-import { ChangeOrderListsTable } from "@/lib/poa/table/poa-table"
-
-interface IndexPageProps {
- searchParams: Promise<SearchParams>
-}
-
-export default async function IndexPage(props: IndexPageProps) {
- const searchParams = await props.searchParams
- const search = searchParamsCache.parse(searchParams)
-
- const validFilters = getValidFilters(search.filters)
-
- const promises = Promise.all([
- getChangeOrders({
- ...search,
- filters: validFilters,
- }),
- ])
-
- return (
- <Shell className="gap-2">
- <div className="flex items-center justify-between space-y-2">
- <div className="flex items-center justify-between space-y-2">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- 변경 PO 확인 및 전자서명
- </h2>
- <p className="text-muted-foreground">
- 발행된 PO의 변경 내역을 확인하고 관리할 수 있습니다.
- </p>
- </div>
- </div>
- </div>
-
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}>
- </React.Suspense>
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={6}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
- shrinkZero
- />
- }
- >
- <ChangeOrderListsTable promises={promises} />
- </React.Suspense>
- </Shell>
- )
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/pq-criteria/[id]/page.tsx b/app/[lng]/sales/(sales)/pq-criteria/[id]/page.tsx
deleted file mode 100644
index 55b1e9df..00000000
--- a/app/[lng]/sales/(sales)/pq-criteria/[id]/page.tsx
+++ /dev/null
@@ -1,81 +0,0 @@
-import * as React from "react"
-import { type SearchParams } from "@/types/table"
-import { getValidFilters } from "@/lib/data-table"
-import { Skeleton } from "@/components/ui/skeleton"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { Shell } from "@/components/shell"
-import { searchParamsCache } from "@/lib/pq/validations"
-import { getPQs } from "@/lib/pq/service"
-import { PqsTable } from "@/lib/pq/table/pq-table"
-import { ProjectSelectorWrapper } from "@/components/pq/project-select-wrapper"
-import { notFound } from "next/navigation"
-
-interface ProjectPageProps {
- params: { id: string }
- searchParams: Promise<SearchParams>
-}
-
-export default async function ProjectPage(props: ProjectPageProps) {
- const resolvedParams = await props.params
- const id = resolvedParams.id
-
- const projectId = parseInt(id, 10)
-
- // 유효하지 않은 projectId 확인
- if (isNaN(projectId)) {
- notFound()
- }
-
- const searchParams = await props.searchParams
- const search = searchParamsCache.parse(searchParams)
-
- // filters가 없는 경우를 처리
- const validFilters = getValidFilters(search.filters)
-
- // 프로젝트별 PQ 데이터 가져오기
- const promises = Promise.all([
- getPQs({
- ...search,
- filters: validFilters,
- }, projectId, false)
- ])
-
- return (
- <Shell className="gap-2">
- <div className="flex items-center justify-between">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- Pre-Qualification Check Sheet
- </h2>
- <p className="text-muted-foreground">
- 협력업체 등록을 위한, 협력업체가 제출할 PQ 항목을: 프로젝트별로 관리할 수 있습니다.
- </p>
- </div>
- <ProjectSelectorWrapper selectedProjectId={projectId} />
- </div>
-
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}>
- {/* <DateRangePicker
- triggerSize="sm"
- triggerClassName="ml-auto w-56 sm:w-60"
- align="end"
- shallow={false}
- /> */}
- </React.Suspense>
-
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={6}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
- shrinkZero
- />
- }
- >
- <PqsTable promises={promises} currentProjectId={projectId}/>
- </React.Suspense>
- </Shell>
- )
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/pq-criteria/page.tsx b/app/[lng]/sales/(sales)/pq-criteria/page.tsx
deleted file mode 100644
index 7785b541..00000000
--- a/app/[lng]/sales/(sales)/pq-criteria/page.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import * as React from "react"
-import { type SearchParams } from "@/types/table"
-import { getValidFilters } from "@/lib/data-table"
-import { Skeleton } from "@/components/ui/skeleton"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { Shell } from "@/components/shell"
-import { searchParamsCache } from "@/lib/pq/validations"
-import { getPQs } from "@/lib/pq/service"
-import { PqsTable } from "@/lib/pq/table/pq-table"
-import { ProjectSelectorWrapper } from "@/components/pq/project-select-wrapper"
-
-interface IndexPageProps {
- searchParams: Promise<SearchParams>
-}
-
-export default async function IndexPage(props: IndexPageProps) {
- const searchParams = await props.searchParams
- const search = searchParamsCache.parse(searchParams)
-
- // filters가 없는 경우를 처리
-
- const validFilters = getValidFilters(search.filters)
-
- // onlyGeneral: true로 설정하여 일반 PQ 항목만 가져옴
- const promises = Promise.all([
- getPQs({
- ...search,
- filters: validFilters,
- }, null, true)
- ])
-
- return (
- <Shell className="gap-2">
- <div className="flex items-center justify-between">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- Pre-Qualification Check Sheet
- </h2>
- <p className="text-muted-foreground">
- 협력업체 등록을 위한, 협력업체가 제출할 PQ 항목을 관리할 수 있습니다.
- </p>
- </div>
- <ProjectSelectorWrapper />
- </div>
-
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}>
- {/* <DateRangePicker
- triggerSize="sm"
- triggerClassName="ml-auto w-56 sm:w-60"
- align="end"
- shallow={false}
- /> */}
- </React.Suspense>
-
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={6}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
- shrinkZero
- />
- }
- >
- <PqsTable promises={promises}/>
- </React.Suspense>
- </Shell>
- )
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/pq/[vendorId]/page.tsx b/app/[lng]/sales/(sales)/pq/[vendorId]/page.tsx
deleted file mode 100644
index 76bcfe59..00000000
--- a/app/[lng]/sales/(sales)/pq/[vendorId]/page.tsx
+++ /dev/null
@@ -1,108 +0,0 @@
-import * as React from "react"
-import { Shell } from "@/components/shell"
-import { type SearchParams } from "@/types/table"
-import { getPQDataByVendorId, getVendorPQsList, loadGeneralPQData, loadProjectPQAction, loadProjectPQData } from "@/lib/pq/service"
-import { Vendor } from "@/db/schema/vendors"
-import { findVendorById } from "@/lib/vendors/service"
-import VendorPQAdminReview from "@/components/pq/pq-review-detail"
-import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
-import { Badge } from "@/components/ui/badge"
-
-interface IndexPageProps {
- params: {
- vendorId: string
- }
- searchParams: Promise<SearchParams>
-}
-
-export default async function PQReviewPage(props: IndexPageProps) {
- const resolvedParams = await props.params
- const vendorId = Number(resolvedParams.vendorId)
-
- // Fetch the vendor data
- const vendor: Vendor | null = await findVendorById(vendorId)
- if (!vendor) return <div>Vendor not found</div>
-
- // Get list of all PQs (general + project-specific) for this vendor
- const pqsList = await getVendorPQsList(vendorId)
-
- // Determine default active PQ to display
- // If query param projectId exists, use that, otherwise use general PQ if available
- const searchParams = await props.searchParams
- const activeProjectId = searchParams.projectId ? Number(searchParams.projectId) : undefined
-
- // If no projectId query param, default to general PQ or first project PQ
- const defaultTabId = activeProjectId ?
- `project-${activeProjectId}` :
- (pqsList.hasGeneralPq ? 'general' : `project-${pqsList.projectPQs[0]?.projectId}`)
-
- // Fetch PQ data for the active tab
- let pqData;
- if (activeProjectId) {
- // Get project-specific PQ data
- pqData = await getPQDataByVendorId(vendorId, activeProjectId)
- } else {
- // Get general PQ data
- pqData = await getPQDataByVendorId(vendorId)
- }
-
- return (
- <Shell className="gap-2">
- {pqsList.hasGeneralPq || pqsList.projectPQs.length > 0 ? (
- <Tabs defaultValue={defaultTabId} className="space-y-4">
- <div className="flex justify-between items-center">
- <h1 className="text-2xl font-bold">
- {vendor.vendorName} PQ Review
- </h1>
-
- <TabsList>
- {pqsList.hasGeneralPq && (
- <TabsTrigger value="general">
- General PQ <Badge variant="outline" className="ml-2">Standard</Badge>
- </TabsTrigger>
- )}
-
- {pqsList.projectPQs.map((project) => (
- <TabsTrigger key={project.projectId} value={`project-${project.projectId}`}>
- {project.projectName} <Badge variant="outline" className="ml-2">{project.status}</Badge>
- </TabsTrigger>
- ))}
- </TabsList>
- </div>
-
- {/* Tab content for General PQ */}
- {pqsList.hasGeneralPq && (
- <TabsContent value="general" className="mt-0">
- <VendorPQAdminReview
- data={activeProjectId ? [] : pqData}
- vendor={vendor}
- projectId={undefined}
- loadData={loadGeneralPQData}
- pqType="general"
- />
- </TabsContent>
- )}
-
- {/* Tab content for each Project PQ */}
- {pqsList.projectPQs.map((project) => (
- <TabsContent key={project.projectId} value={`project-${project.projectId}`} className="mt-0">
- <VendorPQAdminReview
- data={activeProjectId === project.projectId ? pqData : []}
- vendor={vendor}
- projectId={project.projectId}
- projectName={project.projectName}
- projectStatus={project.status}
- loadData={loadProjectPQAction}
- pqType="project"
- />
- </TabsContent>
- ))}
- </Tabs>
- ) : (
- <div className="text-center py-10">
- <h2 className="text-xl font-medium">No PQ submissions found for this vendor</h2>
- </div>
- )}
- </Shell>
- )
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/pq/page.tsx b/app/[lng]/sales/(sales)/pq/page.tsx
deleted file mode 100644
index 46b22b12..00000000
--- a/app/[lng]/sales/(sales)/pq/page.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-import * as React from "react"
-import { type SearchParams } from "@/types/table"
-
-import { getValidFilters } from "@/lib/data-table"
-import { Skeleton } from "@/components/ui/skeleton"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { Shell } from "@/components/shell"
-
-import { getVendorsInPQ } from "@/lib/pq/service"
-import { searchParamsCache } from "@/lib/vendors/validations"
-import { VendorsPQReviewTable } from "@/lib/pq/pq-review-table/vendors-table"
-
-interface IndexPageProps {
- searchParams: Promise<SearchParams>
-}
-
-export default async function IndexPage(props: IndexPageProps) {
- const searchParams = await props.searchParams
- const search = searchParamsCache.parse(searchParams)
-
- const validFilters = getValidFilters(search.filters)
-
- const promises = Promise.all([
- getVendorsInPQ({
- ...search,
- filters: validFilters,
- }),
- ])
-
- return (
- <Shell className="gap-2">
-
- <div className="flex items-center justify-between space-y-2">
- <div className="flex items-center justify-between space-y-2">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- Pre-Qualification Review
- </h2>
- <p className="text-muted-foreground">
- 벤더가 제출한 PQ를 확인하고 수정 요청 등을 할 수 있으며 PQ 종료 후에는 통과 여부를 결정할 수 있습니다.
-
- </p>
- </div>
- </div>
- </div>
-
-
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}>
- {/* <DateRangePicker
- triggerSize="sm"
- triggerClassName="ml-auto w-56 sm:w-60"
- align="end"
- shallow={false}
- /> */}
- </React.Suspense>
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={6}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
- shrinkZero
- />
- }
- >
- <VendorsPQReviewTable promises={promises} />
- </React.Suspense>
- </Shell>
- )
-}
diff --git a/app/[lng]/sales/(sales)/pq_new/[vendorId]/[submissionId]/page.tsx b/app/[lng]/sales/(sales)/pq_new/[vendorId]/[submissionId]/page.tsx
deleted file mode 100644
index 28ce3128..00000000
--- a/app/[lng]/sales/(sales)/pq_new/[vendorId]/[submissionId]/page.tsx
+++ /dev/null
@@ -1,215 +0,0 @@
-import * as React from "react"
-import { Metadata } from "next"
-import Link from "next/link"
-import { notFound } from "next/navigation"
-import { ArrowLeft } from "lucide-react"
-import { Shell } from "@/components/shell"
-import { Button } from "@/components/ui/button"
-import { Badge } from "@/components/ui/badge"
-import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
-import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
-import { Separator } from "@/components/ui/separator"
-import { getPQById, getPQDataByVendorId } from "@/lib/pq/service"
-import { unstable_noStore as noStore } from 'next/cache'
-import { PQReviewWrapper } from "@/components/pq-input/pq-review-wrapper"
-
-export const metadata: Metadata = {
- title: "PQ 검토",
- description: "협력업체의 Pre-Qualification 답변을 검토합니다.",
-}
-
-// 페이지가 기본적으로 동적임을 나타냄
-export const dynamic = "force-dynamic"
-
-interface PQReviewPageProps {
- params: Promise<{
- vendorId: string;
- submissionId: string;
- }>
-}
-
-export default async function PQReviewPage(props: PQReviewPageProps) {
- // 캐시 비활성화
- noStore()
-
- const params = await props.params
- const vendorId = parseInt(params.vendorId, 10)
- const submissionId = parseInt(params.submissionId, 10)
-
- try {
- // PQ Submission 정보 조회
- const pqSubmission = await getPQById(submissionId, vendorId)
-
- // PQ 데이터 조회 (질문과 답변)
- const pqData = await getPQDataByVendorId(vendorId, pqSubmission.projectId || undefined)
-
- // 프로젝트 정보 (프로젝트 PQ인 경우)
- const projectInfo = pqSubmission.projectId ? {
- id: pqSubmission.projectId,
- projectCode: pqSubmission.projectCode || '',
- projectName: pqSubmission.projectName || '',
- status: pqSubmission.status,
- submittedAt: pqSubmission.submittedAt,
- } : null
-
- // PQ 유형 및 상태 레이블
- const typeLabel = pqSubmission.type === "GENERAL" ? "일반 PQ" : "프로젝트 PQ"
- const statusLabel = getStatusLabel(pqSubmission.status)
- const statusVariant = getStatusVariant(pqSubmission.status)
-
- // 수정 가능 여부 (SUBMITTED 상태일 때만 가능)
- const canReview = pqSubmission.status === "SUBMITTED"
-
- return (
- <Shell className="gap-6 max-w-5xl">
- <div className="flex items-center justify-between">
- <div className="flex items-center gap-4">
- <Button variant="outline" size="sm" asChild>
- <Link href="/evcp/pq_new">
- <ArrowLeft className="w-4 h-4 mr-2" />
- 목록으로
- </Link>
- </Button>
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- {pqSubmission.vendorName} - {typeLabel}
- </h2>
- <div className="flex items-center gap-2 mt-1">
- <Badge variant={statusVariant}>{statusLabel}</Badge>
- {projectInfo && (
- <span className="text-muted-foreground">
- {projectInfo.projectName} ({projectInfo.projectCode})
- </span>
- )}
- </div>
- </div>
- </div>
- </div>
-
- {/* 상태별 알림 */}
- {pqSubmission.status === "SUBMITTED" && (
- <Alert>
- <AlertTitle>제출 완료</AlertTitle>
- <AlertDescription>
- 협력업체가 {formatDate(pqSubmission.submittedAt)}에 PQ를 제출했습니다. 검토 후 승인 또는 거부할 수 있습니다.
- </AlertDescription>
- </Alert>
- )}
-
- {pqSubmission.status === "APPROVED" && (
- <Alert variant="success">
- <AlertTitle>승인됨</AlertTitle>
- <AlertDescription>
- {formatDate(pqSubmission.approvedAt)}에 승인되었습니다.
- </AlertDescription>
- </Alert>
- )}
-
- {pqSubmission.status === "REJECTED" && (
- <Alert variant="destructive">
- <AlertTitle>거부됨</AlertTitle>
- <AlertDescription>
- {formatDate(pqSubmission.rejectedAt)}에 거부되었습니다.
- {pqSubmission.rejectReason && (
- <div className="mt-2">
- <strong>사유:</strong> {pqSubmission.rejectReason}
- </div>
- )}
- </AlertDescription>
- </Alert>
- )}
-
- <Separator />
-
- {/* PQ 검토 컴포넌트 */}
- <Tabs defaultValue="review" className="w-full">
- <TabsList>
- <TabsTrigger value="review">PQ 검토</TabsTrigger>
- <TabsTrigger value="vendor-info">협력업체 정보</TabsTrigger>
- </TabsList>
-
- <TabsContent value="review" className="mt-4">
- <PQReviewWrapper
- pqData={pqData}
- vendorId={vendorId}
- pqSubmission={pqSubmission}
- canReview={canReview}
- />
- </TabsContent>
-
- <TabsContent value="vendor-info" className="mt-4">
- <div className="rounded-md border p-4">
- <h3 className="text-lg font-medium mb-4">협력업체 정보</h3>
- <div className="grid grid-cols-2 gap-4">
- <div>
- <p className="text-sm font-medium text-muted-foreground">업체명</p>
- <p>{pqSubmission.vendorName}</p>
- </div>
- <div>
- <p className="text-sm font-medium text-muted-foreground">업체 코드</p>
- <p>{pqSubmission.vendorCode}</p>
- </div>
- <div>
- <p className="text-sm font-medium text-muted-foreground">상태</p>
- <p>{pqSubmission.vendorStatus}</p>
- </div>
- {/* 필요시 추가 정보 표시 */}
- </div>
- </div>
- </TabsContent>
- </Tabs>
- </Shell>
- )
- } catch (error) {
- console.error("Error loading PQ:", error)
- notFound()
- }
-}
-
-// 상태 레이블 함수
-function getStatusLabel(status: string): string {
- switch (status) {
- case "REQUESTED":
- return "요청됨";
- case "IN_PROGRESS":
- return "진행 중";
- case "SUBMITTED":
- return "제출됨";
- case "APPROVED":
- return "승인됨";
- case "REJECTED":
- return "거부됨";
- default:
- return status;
- }
-}
-
-// 상태별 Badge 스타일
-function getStatusVariant(status: string): "default" | "outline" | "secondary" | "destructive" | "success" {
- switch (status) {
- case "REQUESTED":
- return "outline";
- case "IN_PROGRESS":
- return "secondary";
- case "SUBMITTED":
- return "default";
- case "APPROVED":
- return "success";
- case "REJECTED":
- return "destructive";
- default:
- return "outline";
- }
-}
-
-// 날짜 형식화 함수
-function formatDate(date: Date | null) {
- if (!date) return "날짜 없음";
- return new Date(date).toLocaleDateString("ko-KR", {
- year: "numeric",
- month: "long",
- day: "numeric",
- hour: "2-digit",
- minute: "2-digit"
- });
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/pq_new/page.tsx b/app/[lng]/sales/(sales)/pq_new/page.tsx
deleted file mode 100644
index 6598349b..00000000
--- a/app/[lng]/sales/(sales)/pq_new/page.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import * as React from "react"
-import { Metadata } from "next"
-import { type SearchParams } from "@/types/table"
-import { getValidFilters } from "@/lib/data-table"
-import { Shell } from "@/components/shell"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { searchParamsPQReviewCache } from "@/lib/pq/validations"
-import { getPQSubmissions } from "@/lib/pq/service"
-import { PQSubmissionsTable } from "@/lib/pq/pq-review-table-new/vendors-table"
-
-export const metadata: Metadata = {
- title: "PQ 검토/실사 의뢰",
- description: "",
-}
-
-interface PQReviewPageProps {
- searchParams: Promise<SearchParams>
-}
-
-export default async function PQReviewPage(props: PQReviewPageProps) {
- const searchParams = await props.searchParams
- const search = searchParamsPQReviewCache.parse(searchParams)
- const validFilters = getValidFilters(search.filters)
-
- // 디버깅 로그 추가
- console.log("=== PQ Page Debug ===");
- console.log("Raw searchParams:", searchParams);
- console.log("Raw basicFilters param:", searchParams.basicFilters);
- console.log("Raw pqBasicFilters param:", searchParams.pqBasicFilters);
- console.log("Parsed search:", search);
- console.log("search.filters:", search.filters);
- console.log("search.basicFilters:", search.basicFilters);
- console.log("search.pqBasicFilters:", search.pqBasicFilters);
- console.log("validFilters:", validFilters);
-
- // 기본 필터 처리 (통일된 이름 사용)
- let basicFilters = []
- if (search.basicFilters && search.basicFilters.length > 0) {
- basicFilters = search.basicFilters
- console.log("Using search.basicFilters:", basicFilters);
- } else if (search.pqBasicFilters && search.pqBasicFilters.length > 0) {
- // 하위 호환성을 위해 기존 이름도 지원
- basicFilters = search.pqBasicFilters
- console.log("Using search.pqBasicFilters:", basicFilters);
- } else {
- console.log("No basic filters found");
- }
-
- // 모든 필터를 합쳐서 처리
- const allFilters = [...validFilters, ...basicFilters]
-
- console.log("Final allFilters:", allFilters);
-
- // 조인 연산자도 통일된 이름 사용
- const joinOperator = search.basicJoinOperator || search.pqBasicJoinOperator || search.joinOperator || 'and';
- console.log("Final joinOperator:", joinOperator);
-
- // Promise.all로 감싸서 전달
- const promises = Promise.all([
- getPQSubmissions({
- ...search,
- filters: allFilters,
- joinOperator,
- })
- ])
-
- return (
- <Shell className="gap-4">
- <div className="flex items-center justify-between space-y-2">
- <div className="flex items-center justify-between space-y-2">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- PQ 검토/실사 의뢰
- </h2>
- </div>
- </div>
- </div>
-
- {/* Items처럼 직접 테이블 렌더링 */}
- <React.Suspense
- key={JSON.stringify(searchParams)} // URL 파라미터가 변경될 때마다 강제 리렌더링
- fallback={
- <DataTableSkeleton
- columnCount={8}
- searchableColumnCount={2}
- filterableColumnCount={3}
- cellWidths={["10rem", "15rem", "12rem", "12rem", "8rem", "8rem", "10rem", "8rem"]}
- shrinkZero
- />
- }
- >
- <PQSubmissionsTable promises={promises} />
- </React.Suspense>
- </Shell>
- )
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/report/page.tsx b/app/[lng]/sales/(sales)/report/page.tsx
index 33225e33..db1bb9d8 100644
--- a/app/[lng]/sales/(sales)/report/page.tsx
+++ b/app/[lng]/sales/(sales)/report/page.tsx
@@ -1,5 +1,3 @@
-
-// app/procurement/dashboard/page.tsx
import * as React from "react";
import { Skeleton } from "@/components/ui/skeleton";
import { Shell } from "@/components/shell";
@@ -7,29 +5,34 @@ import { ErrorBoundary } from "@/components/error-boundary";
import { getDashboardData } from "@/lib/dashboard/service";
import { DashboardClient } from "@/lib/dashboard/dashboard-client";
-// 대시보드 데이터 로딩 컴포넌트
-async function DashboardContent() {
+export default async function IndexPage() {
+ // domain을 명시적으로 전달
+ const domain = "sales";
+
try {
- const data = await getDashboardData("sales");
+ // 서버에서 직접 데이터 fetch
+ const dashboardData = await getDashboardData(domain);
- const handleRefresh = async () => {
- "use server";
- return await getDashboardData("sales");
- };
-
return (
- <DashboardClient
- initialData={data}
- onRefresh={handleRefresh}
- />
+ <Shell className="gap-2">
+ <DashboardClient initialData={dashboardData} />
+ </Shell>
);
} catch (error) {
- console.error("Dashboard data loading error:", error);
- throw error;
+ console.error("Dashboard data fetch error:", error);
+ return (
+ <Shell className="gap-2">
+ <div className="flex items-center justify-center py-12">
+ <div className="text-center space-y-2">
+ <p className="text-destructive">데이터를 불러오는데 실패했습니다.</p>
+ <p className="text-muted-foreground text-sm">{error instanceof Error ? error.message : "알 수 없는 오류가 발생했습니다."}</p>
+ </div>
+ </div>
+ </Shell>
+ );
}
}
-// 대시보드 로딩 스켈레톤
function DashboardSkeleton() {
return (
<div className="space-y-6">
@@ -95,35 +98,3 @@ function DashboardSkeleton() {
</div>
);
}
-
-// 에러 표시 컴포넌트
-function DashboardError({ error, reset }: { error: Error; reset: () => void }) {
- return (
- <div className="flex flex-col items-center justify-center py-12 space-y-4">
- <div className="text-center space-y-2">
- <h3 className="text-lg font-semibold">대시보드를 불러올 수 없습니다</h3>
- <p className="text-muted-foreground">
- {error.message || "알 수 없는 오류가 발생했습니다."}
- </p>
- </div>
- <button
- onClick={reset}
- className="px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90"
- >
- 다시 시도
- </button>
- </div>
- );
-}
-
-export default async function DashboardPage() {
- return (
- <Shell className="gap-6">
- <ErrorBoundary fallback={DashboardError}>
- <React.Suspense fallback={<DashboardSkeleton />}>
- <DashboardContent />
- </React.Suspense>
- </ErrorBoundary>
- </Shell>
- );
-}
diff --git a/app/[lng]/sales/(sales)/rfq/[id]/cbe/page.tsx b/app/[lng]/sales/(sales)/rfq/[id]/cbe/page.tsx
deleted file mode 100644
index fb288a98..00000000
--- a/app/[lng]/sales/(sales)/rfq/[id]/cbe/page.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import { Separator } from "@/components/ui/separator"
-import { type SearchParams } from "@/types/table"
-import { getValidFilters } from "@/lib/data-table"
-import { searchParamsCBECache } from "@/lib/rfqs/validations"
-import { getCBE } from "@/lib/rfqs/service"
-import { CbeTable } from "@/lib/rfqs/cbe-table/cbe-table"
-
-interface IndexPageProps {
- // Next.js 13 App Router에서 기본으로 주어지는 객체들
- params: {
- lng: string
- id: string
- }
- searchParams: Promise<SearchParams>
-}
-
-export default async function RfqCBEPage(props: IndexPageProps) {
- const resolvedParams = await props.params
- const lng = resolvedParams.lng
- const id = resolvedParams.id
-
- const idAsNumber = Number(id)
-
- // 2) SearchParams 파싱 (Zod)
- // - "filters", "page", "perPage", "sort" 등 contact 전용 컬럼
- const searchParams = await props.searchParams
- const search = searchParamsCBECache.parse(searchParams)
- const validFilters = getValidFilters(search.filters)
-
- const promises = Promise.all([
- getCBE({
- ...search,
- filters: validFilters,
- },
- idAsNumber)
- ])
-
- // 4) 렌더링
- return (
- <div className="space-y-6">
- <div>
- <h3 className="text-lg font-medium">
- Commercial Bid Evaluation
- </h3>
- <p className="text-sm text-muted-foreground">
- 초대된 협력업체에게 CBE를 보낼 수 있습니다. <br />"발행하기" 버튼을 통해 CBE를 전송하면 CBE 내용이 메일로 전달되고 eVCP에도 협력업체가 입력할 수 있게 자동 생성됩니다.
- </p>
- </div>
- <Separator />
- <div>
- <CbeTable promises={promises} rfqId={idAsNumber} />
- </div>
- </div>
- )
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/rfq/[id]/layout.tsx b/app/[lng]/sales/(sales)/rfq/[id]/layout.tsx
deleted file mode 100644
index 9a03efa4..00000000
--- a/app/[lng]/sales/(sales)/rfq/[id]/layout.tsx
+++ /dev/null
@@ -1,89 +0,0 @@
-import { Metadata } from "next"
-import Link from "next/link"
-import { Separator } from "@/components/ui/separator"
-import { SidebarNav } from "@/components/layout/sidebar-nav"
-import { RfqViewWithItems } from "@/db/schema/rfq"
-import { findRfqById } from "@/lib/rfqs/service"
-import { formatDate } from "@/lib/utils"
-import { Button } from "@/components/ui/button"
-import { ArrowLeft } from "lucide-react"
-
-export const metadata: Metadata = {
- title: "Vendor Detail",
-}
-
-export default async function RfqLayout({
- children,
- params,
-}: {
- children: React.ReactNode
- params: { lng: string, id: string }
-}) {
-
- // 1) URL 파라미터에서 id 추출, Number로 변환
- const resolvedParams = await params
- const lng = resolvedParams.lng
- const id = resolvedParams.id
-
- const idAsNumber = Number(id)
- // 2) DB에서 해당 협력업체 정보 조회
- const rfq: RfqViewWithItems | null = await findRfqById(idAsNumber)
-
- // 3) 사이드바 메뉴
- const sidebarNavItems = [
- {
- title: "Matched Vendors",
- href: `/${lng}/evcp/rfq/${id}`,
- },
- {
- title: "TBE",
- href: `/${lng}/evcp/rfq/${id}/tbe`,
- },
- {
- title: "CBE",
- href: `/${lng}/evcp/rfq/${id}/cbe`,
- },
-
- ]
-
- return (
- <>
- <div className="container py-6">
- <section className="overflow-hidden rounded-[0.5rem] border bg-background shadow">
- <div className="hidden space-y-6 p-10 pb-16 md:block">
- <div className="flex items-center justify-end mb-4">
- <Link href={`/${lng}/evcp/rfq`} passHref>
- <Button variant="ghost" className="flex items-center text-primary hover:text-primary/80 transition-colors p-0 h-auto">
- <ArrowLeft className="mr-1 h-4 w-4" />
- <span>RFQ 목록으로 돌아가기</span>
- </Button>
- </Link>
- </div>
- <div className="space-y-0.5">
- {/* 4) 협력업체 정보가 있으면 코드 + 이름 + "상세 정보" 표기 */}
- <h2 className="text-2xl font-bold tracking-tight">
- {rfq
- ? `${rfq.projectCode ?? ""} ${rfq.rfqCode ?? ""} 관리`
- : "Loading RFQ..."}
- </h2>
-
- <p className="text-muted-foreground">
- {rfq
- ? `${rfq.description ?? ""} ${rfq.lines.map(line => line.itemCode).join(", ")}`
- : ""}
- </p>
- <h3>Due Date:{rfq && rfq?.dueDate && <strong>{formatDate(rfq?.dueDate)}</strong>}</h3>
- </div>
- <Separator className="my-6" />
- <div className="flex flex-col space-y-8 lg:flex-row lg:space-x-12 lg:space-y-0">
- <aside className="lg:w-64 flex-shrink-0">
- <SidebarNav items={sidebarNavItems} />
- </aside>
- <div className="lg:w-[calc(100%-16rem)] overflow-auto">{children}</div>
- </div>
- </div>
- </section>
- </div>
- </>
- )
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/rfq/[id]/page.tsx b/app/[lng]/sales/(sales)/rfq/[id]/page.tsx
deleted file mode 100644
index 1a9f4b18..00000000
--- a/app/[lng]/sales/(sales)/rfq/[id]/page.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import { Separator } from "@/components/ui/separator"
-import { type SearchParams } from "@/types/table"
-import { getValidFilters } from "@/lib/data-table"
-import { getMatchedVendors } from "@/lib/rfqs/service"
-import { searchParamsMatchedVCache } from "@/lib/rfqs/validations"
-import { MatchedVendorsTable } from "@/lib/rfqs/vendor-table/vendors-table"
-
-interface IndexPageProps {
- // Next.js 13 App Router에서 기본으로 주어지는 객체들
- params: {
- lng: string
- id: string
- }
- searchParams: Promise<SearchParams>
-}
-
-export default async function RfqPage(props: IndexPageProps) {
- const resolvedParams = await props.params
- const lng = resolvedParams.lng
- const id = resolvedParams.id
-
- const idAsNumber = Number(id)
-
- // 2) SearchParams 파싱 (Zod)
- // - "filters", "page", "perPage", "sort" 등 contact 전용 컬럼
- const searchParams = await props.searchParams
- const search = searchParamsMatchedVCache.parse(searchParams)
- const validFilters = getValidFilters(search.filters)
-
- const promises = Promise.all([
- getMatchedVendors({
- ...search,
- filters: validFilters,
- },
- idAsNumber)
- ])
-
- // 4) 렌더링
- return (
- <div className="space-y-6">
- <div>
- <h3 className="text-lg font-medium">
- Vendors
- </h3>
- <p className="text-sm text-muted-foreground">
- 등록된 협력업체 중에서 이 RFQ 아이템에 매칭되는 업체를 보여줍니다. <br/>"발행하기" 버튼을 통해 RFQ를 전송하면 첨부파일과 함께 RFQ 내용이 메일로 전달되고 eVCP에도 협력업체가 입력할 수 있게 자동 생성됩니다.
- </p>
- </div>
- <Separator />
- <div>
- <MatchedVendorsTable promises={promises} rfqId={idAsNumber}/>
- </div>
- </div>
- )
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/rfq/[id]/tbe/page.tsx b/app/[lng]/sales/(sales)/rfq/[id]/tbe/page.tsx
deleted file mode 100644
index 76eea302..00000000
--- a/app/[lng]/sales/(sales)/rfq/[id]/tbe/page.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import { Separator } from "@/components/ui/separator"
-import { type SearchParams } from "@/types/table"
-import { getValidFilters } from "@/lib/data-table"
-import { getTBE } from "@/lib/rfqs/service"
-import { searchParamsTBECache } from "@/lib/rfqs/validations"
-import { TbeTable } from "@/lib/rfqs/tbe-table/tbe-table"
-
-interface IndexPageProps {
- // Next.js 13 App Router에서 기본으로 주어지는 객체들
- params: {
- lng: string
- id: string
- }
- searchParams: Promise<SearchParams>
-}
-
-export default async function RfqTBEPage(props: IndexPageProps) {
- const resolvedParams = await props.params
- const lng = resolvedParams.lng
- const id = resolvedParams.id
-
- const idAsNumber = Number(id)
-
- // 2) SearchParams 파싱 (Zod)
- // - "filters", "page", "perPage", "sort" 등 contact 전용 컬럼
- const searchParams = await props.searchParams
- const search = searchParamsTBECache.parse(searchParams)
- const validFilters = getValidFilters(search.filters)
-
- const promises = Promise.all([
- getTBE({
- ...search,
- filters: validFilters,
- },
- idAsNumber)
- ])
-
- // 4) 렌더링
- return (
- <div className="space-y-6">
- <div>
- <h3 className="text-lg font-medium">
- Technical Bid Evaluation
- </h3>
- <p className="text-sm text-muted-foreground">
- 초대된 협력업체에게 TBE를 보낼 수 있습니다. <br/>"발행하기" 버튼을 통해 TBE를 전송하면 첨부파일과 함께 TBE 내용이 메일로 전달되고 eVCP에도 협력업체가 입력할 수 있게 자동 생성됩니다.
- </p>
- </div>
- <Separator />
- <div>
- <TbeTable promises={promises} rfqId={idAsNumber}/>
- </div>
- </div>
- )
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/rfq/page.tsx b/app/[lng]/sales/(sales)/rfq/page.tsx
deleted file mode 100644
index 3417b0bf..00000000
--- a/app/[lng]/sales/(sales)/rfq/page.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import * as React from "react"
-import { type SearchParams } from "@/types/table"
-
-import { getValidFilters } from "@/lib/data-table"
-import { Skeleton } from "@/components/ui/skeleton"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { Shell } from "@/components/shell"
-
-import { searchParamsCache } from "@/lib/rfqs/validations"
-import { getRfqs, getRfqStatusCounts } from "@/lib/rfqs/service"
-import { RfqsTable } from "@/lib/rfqs/table/rfqs-table"
-import { getAllItems } from "@/lib/items/service"
-import { RfqType } from "@/lib/rfqs/validations"
-
-interface RfqPageProps {
- searchParams: Promise<SearchParams>;
- rfqType: RfqType;
- title: string;
- description: string;
-}
-
-export default async function RfqPage({
- searchParams,
- rfqType = RfqType.PURCHASE,
- title = "RFQ",
- description = "RFQ를 등록하고 관리할 수 있습니다."
-}: RfqPageProps) {
- const search = searchParamsCache.parse(await searchParams)
-
- const validFilters = getValidFilters(search.filters)
-
- const promises = Promise.all([
- getRfqs({
- ...search,
- filters: validFilters,
- rfqType // 전달받은 rfqType 사용
- }),
- getRfqStatusCounts(rfqType), // rfqType 전달
- getAllItems()
- ])
-
- return (
- <Shell className="gap-2">
- <div className="flex items-center justify-between space-y-2">
- <div className="flex items-center justify-between space-y-2">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- {title}
- </h2>
- <p className="text-muted-foreground">
- {description}
- </p>
- </div>
- </div>
- </div>
-
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}>
- {/* <DateRangePicker
- triggerSize="sm"
- triggerClassName="ml-auto w-56 sm:w-60"
- align="end"
- shallow={false}
- /> */}
- </React.Suspense>
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={6}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
- shrinkZero
- />
- }
- >
- <RfqsTable promises={promises} rfqType={rfqType} />
- </React.Suspense>
- </Shell>
- )
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/tag-numbering/page.tsx b/app/[lng]/sales/(sales)/tag-numbering/page.tsx
deleted file mode 100644
index 44695259..00000000
--- a/app/[lng]/sales/(sales)/tag-numbering/page.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-import * as React from "react"
-import { type SearchParams } from "@/types/table"
-
-import { getValidFilters } from "@/lib/data-table"
-import { Skeleton } from "@/components/ui/skeleton"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { Shell } from "@/components/shell"
-import { searchParamsCache } from "@/lib/tag-numbering/validation"
-import { getTagNumbering } from "@/lib/tag-numbering/service"
-import { TagNumberingTable } from "@/lib/tag-numbering/table/tagNumbering-table"
-
-
-interface IndexPageProps {
- searchParams: Promise<SearchParams>
-}
-
-export default async function IndexPage(props: IndexPageProps) {
- const searchParams = await props.searchParams
- const search = searchParamsCache.parse(searchParams)
-
- const validFilters = getValidFilters(search.filters)
-
- const promises = Promise.all([
- getTagNumbering({
- ...search,
- filters: validFilters,
- }),
-
- ])
-
- return (
- <Shell className="gap-2">
- <div className="flex items-center justify-between space-y-2">
- <div className="flex items-center justify-between space-y-2">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- 태그 타입 목록 from S-EDP
- </h2>
- <p className="text-muted-foreground">
- 태그 넘버링을 위한 룰셋을 S-EDP로부터 가져오고 확인할 수 있습니다{" "}
- {/* <span className="inline-flex items-center whitespace-nowrap">
- <Ellipsis className="size-3" />
- <span className="ml-1">버튼</span>
- </span>
- 을 통해 담당자 연락처, 입찰 이력, 계약 이력, 패키지 내용 등을 확인 할 수 있습니다. */}
- </p>
- </div>
- </div>
- </div>
-
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}>
- {/* <DateRangePicker
- triggerSize="sm"
- triggerClassName="ml-auto w-56 sm:w-60"
- align="end"
- shallow={false}
- /> */}
- </React.Suspense>
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={6}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
- shrinkZero
- />
- }
- >
- <TagNumberingTable promises={promises} />
- </React.Suspense>
- </Shell>
- )
-}
diff --git a/app/[lng]/sales/(sales)/tasks/page.tsx b/app/[lng]/sales/(sales)/tasks/page.tsx
deleted file mode 100644
index 91b946fb..00000000
--- a/app/[lng]/sales/(sales)/tasks/page.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import * as React from "react"
-import { type SearchParams } from "@/types/table"
-
-import { getValidFilters } from "@/lib/data-table"
-import { Skeleton } from "@/components/ui/skeleton"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { DateRangePicker } from "@/components/date-range-picker"
-import { Shell } from "@/components/shell"
-
-import { FeatureFlagsProvider } from "@/lib/tasks/table/feature-flags-provider"
-import { TasksTable } from "@/lib/tasks/table/tasks-table"
-import {
- getTaskPriorityCounts,
- getTasks,
- getTaskStatusCounts,
-} from "@/lib/tasks/service"
-import { searchParamsCache } from "@/lib/tasks/validations"
-
-interface IndexPageProps {
- searchParams: Promise<SearchParams>
-}
-
-export default async function IndexPage(props: IndexPageProps) {
- const searchParams = await props.searchParams
- const search = searchParamsCache.parse(searchParams)
-
- const validFilters = getValidFilters(search.filters)
-
- const promises = Promise.all([
- getTasks({
- ...search,
- filters: validFilters,
- }),
- getTaskStatusCounts(),
- getTaskPriorityCounts(),
- ])
-
- return (
- <Shell className="gap-2">
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}>
- <DateRangePicker
- triggerSize="sm"
- triggerClassName="ml-auto w-56 sm:w-60"
- align="end"
- shallow={false}
- />
- </React.Suspense>
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={6}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
- shrinkZero
- />
- }
- >
- <TasksTable promises={promises} />
- </React.Suspense>
- </Shell>
- )
-}
diff --git a/app/[lng]/sales/(sales)/vendor-check-list/page.tsx b/app/[lng]/sales/(sales)/vendor-check-list/page.tsx
deleted file mode 100644
index 3fd7e425..00000000
--- a/app/[lng]/sales/(sales)/vendor-check-list/page.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-import * as React from "react"
-import { type SearchParams } from "@/types/table"
-
-import { getValidFilters } from "@/lib/data-table"
-import { Skeleton } from "@/components/ui/skeleton"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { Shell } from "@/components/shell"
-import { getGenralEvaluationsSchema } from "@/lib/general-check-list/validation"
-import { GeneralEvaluationsTable } from "@/lib/general-check-list/table/general-check-list-table"
-import { getGeneralEvaluations } from "@/lib/general-check-list/service"
-
-
-interface IndexPageProps {
- searchParams: Promise<SearchParams>
-}
-
-export default async function IndexPage(props: IndexPageProps) {
- const searchParams = await props.searchParams
- const search = getGenralEvaluationsSchema.parse(searchParams)
-
- const validFilters = getValidFilters(search.filters)
-
- const promises = Promise.all([
- getGeneralEvaluations({
- ...search,
- filters: validFilters,
- }),
-
- ])
-
- return (
- <Shell className="gap-2">
- <div className="flex items-center justify-between space-y-2">
- <div className="flex items-center justify-between space-y-2">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- 협력업체 정기평가 체크리스트
- </h2>
- <p className="text-muted-foreground">
- 협력업체 평가에 사용되는 정기평가 체크리스트를 관리{" "}
- {/* <span className="inline-flex items-center whitespace-nowrap">
- <Ellipsis className="size-3" />
- <span className="ml-1">버튼</span>
- </span>
- 을 통해 담당자 연락처, 입찰 이력, 계약 이력, 패키지 내용 등을 확인 할 수 있습니다. */}
- </p>
- </div>
- </div>
- </div>
-
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}>
- {/* <DateRangePicker
- triggerSize="sm"
- triggerClassName="ml-auto w-56 sm:w-60"
- align="end"
- shallow={false}
- /> */}
- </React.Suspense>
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={6}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
- shrinkZero
- />
- }
- >
- <GeneralEvaluationsTable promises={promises} />
- </React.Suspense>
- </Shell>
- )
-}
diff --git a/app/[lng]/sales/(sales)/vendor-investigation/page.tsx b/app/[lng]/sales/(sales)/vendor-investigation/page.tsx
deleted file mode 100644
index c59de869..00000000
--- a/app/[lng]/sales/(sales)/vendor-investigation/page.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import * as React from "react"
-import { type SearchParams } from "@/types/table"
-
-import { getValidFilters } from "@/lib/data-table"
-import { Skeleton } from "@/components/ui/skeleton"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { Shell } from "@/components/shell"
-
-import { VendorsInvestigationTable } from "@/lib/vendor-investigation/table/investigation-table"
-import { getVendorsInvestigation } from "@/lib/vendor-investigation/service"
-import { searchParamsInvestigationCache } from "@/lib/vendor-investigation/validations"
-
-interface IndexPageProps {
- searchParams: Promise<SearchParams>
-}
-
-export default async function IndexPage(props: IndexPageProps) {
- const searchParams = await props.searchParams
- const search = searchParamsInvestigationCache.parse(searchParams)
-
- const validFilters = getValidFilters(search.filters)
-
- const promises = Promise.all([
- getVendorsInvestigation({
- ...search,
- filters: validFilters,
- }),
- ])
-
- return (
- <Shell className="gap-2">
-
- <div className="flex items-center justify-between space-y-2">
- <div className="flex items-center justify-between space-y-2">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- Vendor Investigation Management
- </h2>
- <p className="text-muted-foreground">
- 요청된 Vendor 실사에 대한 스케줄 정보를 관리하고 결과를 입력할 수 있습니다.
-
- </p>
- </div>
- </div>
- </div>
-
-
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}>
- </React.Suspense>
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={6}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
- shrinkZero
- />
- }
- >
- <VendorsInvestigationTable promises={promises}/>
- </React.Suspense>
- </Shell>
- )
-}
diff --git a/app/[lng]/sales/(sales)/vendor-type/page.tsx b/app/[lng]/sales/(sales)/vendor-type/page.tsx
deleted file mode 100644
index 997c0f82..00000000
--- a/app/[lng]/sales/(sales)/vendor-type/page.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import * as React from "react"
-import { type SearchParams } from "@/types/table"
-
-import { getValidFilters } from "@/lib/data-table"
-import { Skeleton } from "@/components/ui/skeleton"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { Shell } from "@/components/shell"
-import { searchParamsCache } from "@/lib/vendor-type/validations"
-import { VendorTypesTable } from "@/lib/vendor-type/table/vendorTypes-table"
-import { getVendorTypes } from "@/lib/vendor-type/service"
-
-
-interface IndexPageProps {
- searchParams: Promise<SearchParams>
-}
-
-export default async function IndexPage(props: IndexPageProps) {
- const searchParams = await props.searchParams
- const search = searchParamsCache.parse(searchParams)
-
- const validFilters = getValidFilters(search.filters)
-
- const promises = Promise.all([
- getVendorTypes({
- ...search,
- filters: validFilters,
- }),
-
- ])
-
- return (
- <Shell className="gap-2">
- <div className="flex items-center justify-between space-y-2">
- <div className="flex items-center justify-between space-y-2">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- 업체 유형
- </h2>
- <p className="text-muted-foreground">
- 업체 유형을 등록하고 관리할 수 있습니다.{" "}
-
- </p>
- </div>
- </div>
- </div>
-
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}>
- {/* <DateRangePicker
- triggerSize="sm"
- triggerClassName="ml-auto w-56 sm:w-60"
- align="end"
- shallow={false}
- /> */}
- </React.Suspense>
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={6}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
- shrinkZero
- />
- }
- >
- <VendorTypesTable promises={promises} />
- </React.Suspense>
- </Shell>
- )
-}
diff --git a/app/[lng]/sales/(sales)/vendors/[id]/info/items/page.tsx b/app/[lng]/sales/(sales)/vendors/[id]/info/items/page.tsx
deleted file mode 100644
index 5d5838c6..00000000
--- a/app/[lng]/sales/(sales)/vendors/[id]/info/items/page.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import { Separator } from "@/components/ui/separator"
-import { getVendorItems } from "@/lib/vendors/service"
-import { type SearchParams } from "@/types/table"
-import { getValidFilters } from "@/lib/data-table"
-import { searchParamsItemCache } from "@/lib/vendors/validations"
-import { VendorItemsTable } from "@/lib/vendors/items-table/item-table"
-
-interface IndexPageProps {
- // Next.js 13 App Router에서 기본으로 주어지는 객체들
- params: {
- lng: string
- id: string
- }
- searchParams: Promise<SearchParams>
-}
-
-export default async function SettingsAccountPage(props: IndexPageProps) {
- const resolvedParams = await props.params
- const lng = resolvedParams.lng
- const id = resolvedParams.id
-
- const idAsNumber = Number(id)
-
- // 2) SearchParams 파싱 (Zod)
- // - "filters", "page", "perPage", "sort" 등 contact 전용 컬럼
- const searchParams = await props.searchParams
- const search = searchParamsItemCache.parse(searchParams)
- const validFilters = getValidFilters(search.filters)
-
-
-
- const promises = Promise.all([
- getVendorItems({
- ...search,
- filters: validFilters,
- },
- idAsNumber)
- ])
- // 4) 렌더링
- return (
- <div className="space-y-6">
- <div>
- <h3 className="text-lg font-medium">
- 공급품목(패키지)
- </h3>
- <p className="text-sm text-muted-foreground">
- {/* 딜리버리가 가능한 아이템 리스트를 확인할 수 있습니다. */}
- </p>
- </div>
- <Separator />
- <div>
- <VendorItemsTable promises={promises} vendorId={idAsNumber}/>
- </div>
- </div>
- )
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/vendors/[id]/info/layout.tsx b/app/[lng]/sales/(sales)/vendors/[id]/info/layout.tsx
deleted file mode 100644
index 7e2cd4f6..00000000
--- a/app/[lng]/sales/(sales)/vendors/[id]/info/layout.tsx
+++ /dev/null
@@ -1,94 +0,0 @@
-import { Metadata } from "next"
-
-import { Separator } from "@/components/ui/separator"
-import { SidebarNav } from "@/components/layout/sidebar-nav"
-import { findVendorById } from "@/lib/vendors/service" // 가정: 여기에 findVendorById가 있다고 가정
-import { Vendor } from "@/db/schema/vendors"
-import { Button } from "@/components/ui/button"
-import { ArrowLeft } from "lucide-react"
-import Link from "next/link"
-export const metadata: Metadata = {
- title: "Vendor Detail",
-}
-
-export default async function SettingsLayout({
- children,
- params,
-}: {
- children: React.ReactNode
- params: { lng: string , id: string}
-}) {
-
- // 1) URL 파라미터에서 id 추출, Number로 변환
- const resolvedParams = await params
- const lng = resolvedParams.lng
- const id = resolvedParams.id
-
- const idAsNumber = Number(id)
- // 2) DB에서 해당 협력업체 정보 조회
- const vendor: Vendor | null = await findVendorById(idAsNumber)
-
- // 3) 사이드바 메뉴
- const sidebarNavItems = [
- {
- title: "연락처",
- href: `/${lng}/evcp/vendors/${id}/info`,
- },
- {
- title: "공급품목(패키지)",
- href: `/${lng}/evcp/vendors/${id}/info/items`,
- },
- {
- title: "공급품목(자재그룹)",
- href: `/${lng}/evcp/vendors/${id}/info/materials`,
- },
- {
- title: "견적 히스토리",
- href: `/${lng}/evcp/vendors/${id}/info/rfq-history`,
- },
- {
- title: "입찰 히스토리",
- href: `/${lng}/evcp/vendors/${id}/info/bid-history`,
- },
- {
- title: "계약 히스토리",
- href: `/${lng}/evcp/vendors/${id}/info/contract-history`,
- },
- ]
-
- return (
- <>
- <div className="container py-6">
- <section className="overflow-hidden rounded-[0.5rem] border bg-background shadow">
- <div className="hidden space-y-6 p-10 pb-16 md:block">
- {/* RFQ 목록으로 돌아가는 링크 추가 */}
- <div className="flex items-center justify-end mb-4">
- <Link href={`/${lng}/evcp/vendors`} passHref>
- <Button variant="ghost" className="flex items-center text-primary hover:text-primary/80 transition-colors p-0 h-auto">
- <ArrowLeft className="mr-1 h-4 w-4" />
- <span>협력업체 목록으로 돌아가기</span>
- </Button>
- </Link>
- </div>
- <div className="space-y-0.5">
- {/* 4) 협력업체 정보가 있으면 코드 + 이름 + "상세 정보" 표기 */}
- <h2 className="text-2xl font-bold tracking-tight">
- {vendor
- ? `${vendor.vendorCode ?? ""} - ${vendor.vendorName} 상세 정보`
- : "Loading Vendor..."}
- </h2>
- <p className="text-muted-foreground">협력업체 관련 상세사항을 확인하세요.</p>
- </div>
- <Separator className="my-6" />
- <div className="flex flex-col space-y-8 lg:flex-row lg:space-x-12 lg:space-y-0">
- <aside className="-mx-4 lg:w-1/5">
- <SidebarNav items={sidebarNavItems} />
- </aside>
- <div className="flex-1">{children}</div>
- </div>
- </div>
- </section>
- </div>
- </>
- )
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/vendors/[id]/info/materials/page.tsx b/app/[lng]/sales/(sales)/vendors/[id]/info/materials/page.tsx
deleted file mode 100644
index 0ebb66ba..00000000
--- a/app/[lng]/sales/(sales)/vendors/[id]/info/materials/page.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import { Separator } from "@/components/ui/separator"
-import { type SearchParams } from "@/types/table"
-import { getValidFilters } from "@/lib/data-table"
-import { searchParamsMaterialCache } from "@/lib/vendors/validations"
-import { getVendorMaterials } from "@/lib/vendors/service"
-import { VendorMaterialsTable } from "@/lib/vendors/materials-table/item-table"
-
-interface IndexPageProps {
- // Next.js 13 App Router에서 기본으로 주어지는 객체들
- params: {
- lng: string
- id: string
- }
- searchParams: Promise<SearchParams>
-}
-
-export default async function SettingsAccountPage(props: IndexPageProps) {
- const resolvedParams = await props.params
- const lng = resolvedParams.lng
- const id = resolvedParams.id
-
- const idAsNumber = Number(id)
-
- // 2) SearchParams 파싱 (Zod)
- // - "filters", "page", "perPage", "sort" 등 contact 전용 컬럼
- const searchParams = await props.searchParams
- const search = searchParamsMaterialCache.parse(searchParams)
- const validFilters = getValidFilters(search.filters)
-
-
-
- const promises = Promise.all([
- getVendorMaterials({
- ...search,
- filters: validFilters,
- },
- idAsNumber)
- ])
- // 4) 렌더링
- return (
- <div className="space-y-6">
- <div>
- <h3 className="text-lg font-medium">
- 공급품목(자재 그룹)
- </h3>
- <p className="text-sm text-muted-foreground">
- {/* 딜리버리가 가능한 공급품목(자재 그룹)을 확인할 수 있습니다. */}
- </p>
- </div>
- <Separator />
- <div>
- <VendorMaterialsTable promises={promises} vendorId={idAsNumber}/>
- </div>
- </div>
- )
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/vendors/[id]/info/page.tsx b/app/[lng]/sales/(sales)/vendors/[id]/info/page.tsx
deleted file mode 100644
index 6279e924..00000000
--- a/app/[lng]/sales/(sales)/vendors/[id]/info/page.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import { Separator } from "@/components/ui/separator"
-import { getVendorContacts } from "@/lib/vendors/service"
-import { type SearchParams } from "@/types/table"
-import { getValidFilters } from "@/lib/data-table"
-import { searchParamsContactCache } from "@/lib/vendors/validations"
-import { VendorContactsTable } from "@/lib/vendors/contacts-table/contact-table"
-
-interface IndexPageProps {
- // Next.js 13 App Router에서 기본으로 주어지는 객체들
- params: {
- lng: string
- id: string
- }
- searchParams: Promise<SearchParams>
-}
-
-export default async function SettingsAccountPage(props: IndexPageProps) {
- const resolvedParams = await props.params
- const lng = resolvedParams.lng
- const id = resolvedParams.id
-
- const idAsNumber = Number(id)
-
- // 2) SearchParams 파싱 (Zod)
- // - "filters", "page", "perPage", "sort" 등 contact 전용 컬럼
- const searchParams = await props.searchParams
- const search = searchParamsContactCache.parse(searchParams)
- const validFilters = getValidFilters(search.filters)
-
-
-
- const promises = Promise.all([
- getVendorContacts({
- ...search,
- filters: validFilters,
- },
- idAsNumber)
- ])
- // 4) 렌더링
- return (
- <div className="space-y-6">
- <div>
- <h3 className="text-lg font-medium">
- Contacts
- </h3>
- <p className="text-sm text-muted-foreground">
- 업무별 담당자 정보를 확인하세요.
- </p>
- </div>
- <Separator />
- <div>
- <VendorContactsTable promises={promises} vendorId={idAsNumber}/>
- </div>
- </div>
- )
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/vendors/[id]/info/rfq-history/page.tsx b/app/[lng]/sales/(sales)/vendors/[id]/info/rfq-history/page.tsx
deleted file mode 100644
index c7f8f8b6..00000000
--- a/app/[lng]/sales/(sales)/vendors/[id]/info/rfq-history/page.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import { Separator } from "@/components/ui/separator"
-import { getRfqHistory } from "@/lib/vendors/service"
-import { type SearchParams } from "@/types/table"
-import { getValidFilters } from "@/lib/data-table"
-import { searchParamsRfqHistoryCache } from "@/lib/vendors/validations"
-import { VendorRfqHistoryTable } from "@/lib/vendors/rfq-history-table/rfq-history-table"
-
-interface IndexPageProps {
- // Next.js 13 App Router에서 기본으로 주어지는 객체들
- params: {
- lng: string
- id: string
- }
- searchParams: Promise<SearchParams>
-}
-
-export default async function RfqHistoryPage(props: IndexPageProps) {
- const resolvedParams = await props.params
- const lng = resolvedParams.lng
- const id = resolvedParams.id
-
- const idAsNumber = Number(id)
-
- // 2) SearchParams 파싱 (Zod)
- // - "filters", "page", "perPage", "sort" 등 contact 전용 컬럼
- const searchParams = await props.searchParams
- const search = searchParamsRfqHistoryCache.parse(searchParams)
- const validFilters = getValidFilters(search.filters)
-
- const promises = Promise.all([
- getRfqHistory({
- ...search,
- filters: validFilters,
- },
- idAsNumber)
- ])
-
- // 4) 렌더링
- return (
- <div className="space-y-6">
- <div>
- <h3 className="text-lg font-medium">
- RFQ History
- </h3>
- <p className="text-sm text-muted-foreground">
- 협력업체의 RFQ 참여 이력을 확인할 수 있습니다.
- </p>
- </div>
- <Separator />
- <div>
- <VendorRfqHistoryTable promises={promises} />
- </div>
- </div>
- )
-} \ No newline at end of file
diff --git a/app/[lng]/sales/(sales)/vendors/page.tsx b/app/[lng]/sales/(sales)/vendors/page.tsx
deleted file mode 100644
index 52af0709..00000000
--- a/app/[lng]/sales/(sales)/vendors/page.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import * as React from "react"
-import { type SearchParams } from "@/types/table"
-
-import { getValidFilters } from "@/lib/data-table"
-import { Skeleton } from "@/components/ui/skeleton"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { Shell } from "@/components/shell"
-
-
-import { searchParamsCache } from "@/lib/vendors/validations"
-import { getVendors, getVendorStatusCounts } from "@/lib/vendors/service"
-import { VendorsTable } from "@/lib/vendors/table/vendors-table"
-import { Ellipsis } from "lucide-react"
-
-interface IndexPageProps {
- searchParams: Promise<SearchParams>
-}
-
-export default async function IndexPage(props: IndexPageProps) {
- const searchParams = await props.searchParams
- const search = searchParamsCache.parse(searchParams)
-
- const validFilters = getValidFilters(search.filters)
-
- const promises = Promise.all([
- getVendors({
- ...search,
- filters: validFilters,
- }),
- getVendorStatusCounts(),
- ])
-
- return (
- <Shell className="gap-2">
-
- <div className="flex items-center justify-between space-y-2">
- <div className="flex items-center justify-between space-y-2">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- 협력업체 리스트
- </h2>
- <p className="text-muted-foreground">
- 협력업체에 대한 요약 정보를 확인하고{" "}
- <span className="inline-flex items-center whitespace-nowrap">
- <Ellipsis className="size-3" />
- <span className="ml-1">버튼</span>
- </span>
- 을 통해 담당자 연락처, 입찰 이력, 계약 이력, 패키지 내용 등을 확인 할 수 있습니다. <br/>벤더의 상태에 따라 가입을 승인해주거나 PQ 요청을 할 수 있고 검토가 완료된 벤더를 기간계 시스템에 전송하여 협력업체 코드를 따올 수 있습니다.
- </p>
- </div>
- </div>
- </div>
-
-
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}>
- {/* <DateRangePicker
- triggerSize="sm"
- triggerClassName="ml-auto w-56 sm:w-60"
- align="end"
- shallow={false}
- /> */}
- </React.Suspense>
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={6}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
- shrinkZero
- />
- }
- >
- <VendorsTable promises={promises} />
- </React.Suspense>
- </Shell>
- )
-}